package org.metawatch.manager;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import org.metawatch.manager.R;
import org.metawatch.manager.R.drawable;
import org.metawatch.manager.R.id;
import org.metawatch.manager.R.layout;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.MapView.LayoutParams;
import com.google.android.maps.*;
import android.location.*;

import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

@SuppressWarnings("unused")
public class _4911Directions extends MapActivity {
	MapView map;
	String str = "temp";
	Location current;
	MyLocationOverlay myLocationOverlay;
	LocationManager lm;
	LocationListener ll;
	String towers;
	int x, y;
	long start, stop;
	GeoPoint touchedPoint, startPoint;
	List<Overlay> overlayList;
	Drawable dr;
	String coord;
	ArrayList<GeoPoint> steps;
	ArrayList<GeoPoint> stepsFinal;
	String display;
	String nextGeo;
	String path2 = "";
	OverlayItem overlayItem2;
	_4911CustomPinpoint custom2;
	ArrayList<String> streets;
	ArrayList<String> streetsFinal;
	Point point1, point2;
	public static final int E6 = 1000000;
	Location currentLocation;
	LocationManager mlocManager;
	LocationListener mlocListener;
	double dlon, dlat, angle, degrees;
	ArrayList<Integer> finalPoints;
	String directions = "";
	ArrayList<LocationPoint> a;
	/*
	 * Initializes the map, and starts the GPS service.
	 * Zooms in on the current location
	 */
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		map = (MapView) findViewById(R.id.mvMain);
		map.setBuiltInZoomControls(true);
		dr = getResources().getDrawable(R.drawable.mappin_blue);
		streets = new ArrayList<String>();
		mlocManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		mlocListener = new mylocationlistener();

		String mlocProvider;
		Criteria hdCrit = new Criteria();
		hdCrit.setAccuracy(Criteria.ACCURACY_FINE);
		mlocProvider = mlocManager.getBestProvider(hdCrit, true);
		mlocManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
				20, 1, mlocListener);
		currentLocation = mlocManager
				.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
		mlocManager.removeUpdates(mlocListener);

		double currentLatitude = currentLocation.getLatitude();
		double currentLongitude = currentLocation.getLongitude();
		str = "\n CurrentLocation: " + "\n Latitude: "
				+ currentLocation.getLatitude() + "\n Longitude: "
				+ currentLocation.getLongitude();
		Log.d("LOCATION CHANGED", currentLocation.getLatitude() + "");
		finalPoints = new ArrayList<Integer>();

		myLocationOverlay = new MyLocationOverlay(this, map);
		map.getOverlays().add(myLocationOverlay);
		myLocationOverlay.enableCompass(); // if you want to display a compass
											// also
		myLocationOverlay.enableMyLocation();

		int latE6 = toDegreesE6(currentLatitude);
		int lonE6 = toDegreesE6(currentLongitude);
		startPoint = new GeoPoint(latE6, lonE6);
		map.getController().animateTo(startPoint);
		map.getController().setZoom(15);
		 /*
         * Enables Touch screen on Maps
         */
		Touch t = new Touch();
		overlayList = map.getOverlays();
		overlayList.add(t);

		steps = new ArrayList<GeoPoint>();
		 /*
         * Button to Display the Current Location Toast
         */
		Button button = (Button) findViewById(R.id.button1);
		button.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT)
						.show();

			}
		});
		 /*
         * Button to Initiate GPS 
         */
		Button button3 = (Button) findViewById(R.id.button2);
		button3.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {

				updateGPS();

			}
		});
		Button button2 = (Button) findViewById(R.id.button3);
		button2.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {

				// Iterator<GeoPoint> i = steps.iterator();

				// Toast.makeText(_4911Directions.this, path2,
				// Toast.LENGTH_SHORT).show();
			}
		});
	} // ON CREATE END
	
	public void updateGPS(){
		directions = "";
		int distance = 0;
		int currentIndex = 0;
		//Current index is the point next in line from current location
		for (int i = 0; i < a.size(); i++) {
			if ((getDistance(startPoint, a.get(i).getGeo()) < 3)) {
				currentIndex = i;
				break;
			}
		}

		if (currentIndex < a.size() - 1) {

			//returns the angle based on the dotproduct of vectors and shows the direction
			angle = function2(startPoint,a.get(currentIndex).getGeo(), touchedPoint);

		}
		angle = -1 * angle;
		String direction = "";
		if (angle > 0) {
			direction = "Right";
		} else {
			direction = "Left";
		}
		Log.d("curIndex", "Current Index=" + currentIndex);
		if (a.size() > 1) {
			directions = ""
					+ a.get(currentIndex).getGeo().toString()
					+ " \n"
					+ a.get(currentIndex).getAdd()
					+ "Distance: "
					+ getDistance(startPoint, a.get(currentIndex)
							.getGeo()) + " meters" + "\n Degrees: "
					+ direction + " " + angle + "\n" + " \n";
		}
		//Toast.makeText(getApplicationContext(), directions,
				//Toast.LENGTH_SHORT).show();
	}
	/*
	 * Location Class which stores the GeoPoint and its corresponding Street Address used in GPS function
	 */
	public class LocationPoint {
		GeoPoint tempLoc;
		String add;

		public LocationPoint() {
			tempLoc = new GeoPoint(0, 0);
			add = "";
		}

		public LocationPoint(GeoPoint a, String b) {
			tempLoc = a;
			add = b;
		}

		public GeoPoint getGeo() {
			return tempLoc;
		}

		public String getAdd() {
			return add;
		}
	}

	protected void onResume() {
		super.onResume();

		mlocManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
				5000, 0, mlocListener);
		mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000,
				0, mlocListener);
	}

	protected void onPause() {
		super.onPause();

		mlocManager.removeUpdates(mlocListener);
		mlocManager.removeUpdates(mlocListener);
	}

	public void calDir() {
		path2 = "";
		// ArrayList<GeoPoint> h = new ArrayList<GeoPoint>();
		HashSet<GeoPoint> h = new HashSet<GeoPoint>();
		h.addAll(steps);
		steps.clear();
		steps.addAll(h);

		Geocoder gcd = new Geocoder(getBaseContext(), Locale.getDefault());

		for (int i = 0; i < steps.size(); i++) {
			try {
				List<Address> address = gcd.getFromLocation(steps.get(i)
						.getLatitudeE6() / 1E6,
						steps.get(i).getLongitudeE6() / 1E6, 1);

				if (address.size() > 0) {
					display = "";
					// for(int j=0; j<address.get(0).getMaxAddressLineIndex();
					// j++){
					display = address.get(0).getAddressLine(0) + "\n";
					display = display.substring(display.indexOf(' ') + 1);

					// }
				}
				streets.add(display);
				// Log.d("streets", "St Name=" + display + " "+steps.get(i));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {

			}

			// path2 = ""+path2+steps.get(i).toString()+
			// " \n"+streets.get(i)+
			// "Distance: "+getDistance(startPoint, steps.get(i))+ " meters"+
			// " \n";

		}
		a = new ArrayList<LocationPoint>();
		for (int c = 0; c < steps.size(); c++) {
			LocationPoint b = new LocationPoint(steps.get(c), streets.get(c));
			a.add(b);
		}
		ArrayList<LocationPoint> c = new ArrayList<LocationPoint>();

		ArrayList<String> tempAdd = new ArrayList<String>();
		for (int f = 0; f < a.size(); f++) {
			if (f < 3 && (f + 1) < a.size()) {
				c.add(a.get(f));
			} else {
				if ((f + 1) < a.size()) {
					if (a.get(f - 1).getAdd().equals(a.get(f).getAdd())) {
						if (a.get(f + 1).getAdd().equals(a.get(f).getAdd())) {

						} else {
							c.add(a.get(f));
						}
					}
				}
			}
		}
		a.clear();
		a.addAll(c);
		for (int q = 0; q < a.size(); q++) {
			OverlayItem overlayItem2 = new OverlayItem(a.get(q).getGeo(), "",
					"");
			_4911CustomPinpoint custom3 = new _4911CustomPinpoint(dr,
					_4911Directions.this);
			custom3.insertPinpoint(overlayItem2);
			overlayList.add(custom3);
			// Log.d("angle", "Repeat = "+q+ " "+ arrStreet.get(q));
		}
	}
	/*
	 * Makes 2 vectors from A to B, and from B to C, and calculates dotproduct between them to return the angle
	 */
	public double function2(GeoPoint a, GeoPoint b, GeoPoint c) {
		float v1x = (float) (b.getLongitudeE6() / 1E6 - a.getLongitudeE6() / 1E6);
		float v1y = (float) (b.getLatitudeE6() / 1E6 - a.getLatitudeE6() / 1E6);

		float v2x = (float) (c.getLongitudeE6() / 1E6 - b.getLongitudeE6() / 1E6);
		float v2y = (float) (c.getLatitudeE6() / 1E6 - b.getLatitudeE6() / 1E6);

		// need to normalize:
		float l1 = (float) Math.sqrt(v1x * v1x + v1y * v1y);
		v1x /= l1;
		v1y /= l1;

		// need to normalize:
		float l2 = (float) Math.sqrt(v2x * v2x + v2y * v2y);
		v2x /= l2;
		v2y /= l2;

		// double rad = Math.acos( v1x * v2x + v1y * v2y );
		double rad = Math.atan2(v2y, v2x) - Math.atan2(v1y, v1x);
		double degres = Math.toDegrees(rad);
		return degres;
	}
	/*
	 * Sets up the Arraylist that contains the GeoPoints of the path that is used when button 3 is pressed
	 */
	public double getDistance(GeoPoint pointA, GeoPoint pointB) {
		double distance;
		Location locationA = new Location("point A");

		locationA.setLatitude(pointA.getLatitudeE6() / 1E6);
		locationA.setLongitude(pointB.getLongitudeE6() / 1E6);

		Location locationB = new Location("point B");

		locationB.setLatitude(pointB.getLatitudeE6() / 1E6);
		locationB.setLongitude(pointB.getLongitudeE6() / 1E6);

		DecimalFormat twoDForm = new DecimalFormat("#.##");

		distance = locationA.distanceTo(locationB);
		distance = Double.valueOf(twoDForm.format(distance));
		return distance;

	}
	/*
	 * Converts the Longitude/ Latitude to GeoPoint format
	 */
	public static int toDegreesE6(double degrees) {
		return (int) Math.round(degrees * E6);
	}
	/*
	  * Initiated when pressed on screen for 1.5 seconds
	  */
	public class Touch extends Overlay {
		public boolean onTouchEvent(MotionEvent e, MapView m) {
			if (e.getAction() == MotionEvent.ACTION_DOWN) {
				start = e.getEventTime();
				x = (int) e.getX();
				y = (int) e.getY();
				touchedPoint = map.getProjection().fromPixels(x, y);
			}

			if (e.getAction() == MotionEvent.ACTION_UP) {
				stop = e.getEventTime();
			}

			if (stop - start > 1000) {
				// v.vibrate(300);
				AlertDialog alert = new AlertDialog.Builder(
						_4911Directions.this).create();
				alert.setTitle("Pick an Option");
				alert.setMessage("Pick one from below");
				/*
				 * Places a pin point at the touched location
				 */
				alert.setButton("Place a Pinpoint",
						new DialogInterface.OnClickListener() {

							public void onClick(DialogInterface dialog,
									int which) {
								// TODO Auto-generated method stub
								OverlayItem overlayItem = new OverlayItem(
										touchedPoint, "", "");
								_4911CustomPinpoint custom = new _4911CustomPinpoint(
										dr, _4911Directions.this);
								custom.insertPinpoint(overlayItem);
								overlayList.add(custom);
							}
						});
				/*
				 * Gets the address of the touched location
				 */
				alert.setButton2("Get Address",
						new DialogInterface.OnClickListener() {

							public void onClick(DialogInterface dialog,
									int which) {
								// TODO Auto-generated method stub
								Geocoder geocoder = new Geocoder(
										getBaseContext(), Locale.getDefault());
								try {
									List<Address> address = geocoder.getFromLocation(
											touchedPoint.getLatitudeE6() / 1E6,
											touchedPoint.getLongitudeE6() / 1E6,
											1);

									if (address.size() > 0) {
										display = "";
										for (int i = 0; i < address.get(0)
												.getMaxAddressLineIndex(); i++) {
											display += address.get(0)
													.getAddressLine(i) + "\n";
										}
										Toast t = Toast.makeText(
												getBaseContext(), display,
												Toast.LENGTH_LONG);
										t.show();
									}
								} catch (IOException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								} finally {

								}
							}
						});
				/*
				 * Gets the direction from current location to touched point and Draws path and initializes the GPS functionality
				 */
				alert.setButton3("Get _4911Directions",
						new DialogInterface.OnClickListener() {

							public void onClick(DialogInterface dialog,
									int which) {
								/*
								 * // TODO Auto-generated method stub
								 * if(map.isSatellite()){
								 * map.setSatellite(false);
								 * map.setStreetView(true); }else {
								 * map.setStreetView(false);
								 * map.setSatellite(true); }
								 */
								OverlayItem overlayItem = new OverlayItem(
										touchedPoint, "", "");
								_4911CustomPinpoint custom = new _4911CustomPinpoint(
										dr, _4911Directions.this);
								custom.insertPinpoint(overlayItem);
								overlayList.add(custom);

								DrawPath(startPoint, touchedPoint, Color.GREEN,
										map);

								calDir();
							}
						});

				alert.show();
				return true;
			}

			return false;
		}
	}
	/*
	 * Uses google Maps service to get the KML route that needs to be parsed and draws the map path Overlay
	 */
	private void DrawPath(GeoPoint src, GeoPoint dest, int color,
			MapView mMapView01) {

		// connect to map web service
		StringBuilder urlString = new StringBuilder();
		urlString.append("http://maps.google.com/maps?f=d&hl=en");
		urlString.append("&saddr=");// from
		urlString.append(Double.toString((double) src.getLatitudeE6() / 1.0E6));
		urlString.append(",");
		urlString
				.append(Double.toString((double) src.getLongitudeE6() / 1.0E6));
		urlString.append("&daddr=");// to
		urlString
				.append(Double.toString((double) dest.getLatitudeE6() / 1.0E6));
		urlString.append(",");
		urlString
				.append(Double.toString((double) dest.getLongitudeE6() / 1.0E6));
		urlString.append("&ie=UTF8&0&om=0&output=kml");

		// Log.d("xxx", "URL=" + urlString.toString());

		// get the kml (XML) doc. And parse it to get the coordinates(direction
		// route).
		Document doc = null;
		HttpURLConnection urlConnection = null;
		URL url = null;
		try {
			url = new URL(urlString.toString());
			urlConnection = (HttpURLConnection) url.openConnection();
			urlConnection.setRequestMethod("GET");
			urlConnection.setDoOutput(true);
			urlConnection.setDoInput(true);
			urlConnection.connect();

			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			doc = db.parse(urlConnection.getInputStream());

			if (doc.getElementsByTagName("GeometryCollection").getLength() > 0) {

				// String path =
				// doc.getElementsByTagName("GeometryCollection").item(0).getFirstChild().getFirstChild().getNodeName();
				String path = doc.getElementsByTagName("GeometryCollection")
						.item(0).getFirstChild().getFirstChild()
						.getFirstChild().getNodeValue();

				// Log.d("xxx", "path=" + path);

				String[] pairs = path.split(" ");
				String[] lngLat = pairs[0].split(","); // lngLat[0]=longitude
														// lngLat[1]=latitude
														// lngLat[2]=height

				// src
				GeoPoint startGP = new GeoPoint(
						(int) (Double.parseDouble(lngLat[1]) * 1E6),
						(int) (Double.parseDouble(lngLat[0]) * 1E6));
				mMapView01.getOverlays().add(
						new _4911MyOverLay(startGP, startGP, 1));

				GeoPoint gp1;
				GeoPoint gp2 = startGP;
				for (int i = 1; i < pairs.length; i++) // the last one would be
														// crash
				{
					lngLat = pairs[i].split(",");

					gp1 = gp2;
					// watch out! For GeoPoint, first:latitude, second:longitude
					double lats = gp2.getLatitudeE6() / 1E6;
					double longs = gp2.getLongitudeE6() / 1E6;

					coord += " " + lats + "," + longs + " /n";
					gp2 = new GeoPoint(
							(int) (Double.parseDouble(lngLat[1]) * 1E6),
							(int) (Double.parseDouble(lngLat[0]) * 1E6));
					mMapView01.getOverlays().add(
							new _4911MyOverLay(gp1, gp2, 2, color));

					steps.add(gp2);
					// Log.d("xxx", "pair:" + pairs[i]);
				}

				// steps.add(dest);
				mMapView01.getOverlays().add(new _4911MyOverLay(dest, dest, 3));

			}
		} catch (MalformedURLException e) {

			e.printStackTrace();
		} catch (IOException e) {

			e.printStackTrace();
		} catch (ParserConfigurationException e) {

			e.printStackTrace();

		} catch (SAXException e) {

			e.printStackTrace();
		}

	}

	protected boolean isRouteDisplayed() {
		// TODO Auto-generated method stub
		return false;
	}

	public class mylocationlistener implements LocationListener {

		public void onLocationChanged(Location loc) {

			// Location location = loc;
			loc.getLatitude();
			loc.getLongitude();
			// currentLocation = loc;
			// Log.d("Lat CHANGED2", loc.getLatitude() + "");
			// Log.d("long CHANGED3", loc.getLongitude() + "");
			str = "\n Location Update: " + "\n Latitude: " + loc.getLatitude()
					+ "\n Longitude: " + loc.getLongitude();

			// Toast.makeText(_4911Directions.this,str,Toast.LENGTH_LONG).show();
			int laE6 = toDegreesE6(loc.getLatitude());
			int loE6 = toDegreesE6(loc.getLongitude());
			startPoint = new GeoPoint(laE6, loE6);
			overlayList.remove(custom2);
			overlayItem2 = new OverlayItem(startPoint, "", "");
			custom2 = new _4911CustomPinpoint(dr, _4911Directions.this);
			custom2.insertPinpoint(overlayItem2);
			overlayList.add(custom2);
		}

		public void onProviderDisabled(String provider) {

			Toast.makeText(_4911Directions.this, "Error onProviderDisabled",
					Toast.LENGTH_LONG).show();
		}

		public void onProviderEnabled(String provider) {

			Toast.makeText(_4911Directions.this, "onProviderEnabled",
					Toast.LENGTH_LONG).show();
		}

		public void onStatusChanged(String provider, int status, Bundle extras) {
			// Toast.makeText(_4911Directions.this,"onStatusChanged",Toast.LENGTH_LONG).show();
		}

	}
}